/*
 ============================================================================
 Name        : elfProcess.c
 Author      :
 Version     :	1
 Copyright   : Your copyright notice
 Description : Process to identifies the elf headers and the different section of the input file
 ============================================================================
 */

#include "elfProcess.h"

#include <libelf.h>
# include <err.h>
# include <fcntl.h>
# include <libelf.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
#include <assert.h>

#define PRINT_FMT 	"%-20s 0x%jx\n" ///<Sets the output format screen attributes ELF header

void ReadingELFExecutableHeader(Elf *e, int fd);
//char rawPrint(unsigned char* buffer, unsigned long size)
/*! \fn rawPrint(int fd)
 \brief Print of display file content on hex.
 \param fd pointer to the file to be process
 */
char rawPrint(int fd)
{

	printf("-------------------------\n");
	printf("        DUMP HEX\n");
	printf("-------------------------\n");
	printf("ADDRESS :  [    DATA    ]\n");
	int pos = 0;

	unsigned char buf[4];
	while (read(fd, &buf, 4))
	{
		printf("%08x: ", pos);
		printf(" %02x ", buf[0]);
		printf(" %02x ", buf[1]);
		printf(" %02x ", buf[2]);
		printf(" %02x\n", buf[3]);
		pos += 4;
	}

	return STATUS_SUCCESS;
}
/*! \fn validateElf(Elf** source, int fd, size_t *shstrndx)
 \brief Validate if the input file is an elf object
 \param source Output parameter that contains the elf object to work with
 \param fd Pointer to the input file
 \param shstrndx Number of section on the file
 */
char validateElf(Elf** source, int fd, size_t *shstrndx)
{
	Elf * e = 0;

	//A call to elf version is mandatory before other functions in the ELF library can be invoked
	if (elf_version(EV_CURRENT) == EV_NONE)
	{
		PRINT_ERROR("Elf lib initialization \n");
		return STATUS_ERROR;
	}
	//elf_begin()takes an open file descriptor and converts it an Elf handle according to the command specified (ELF_C_READ).
	if ((e = elf_begin(fd, ELF_C_READ, NULL )) == NULL )
	{
		PRINT_ERROR("Elf begin failed \n");
		return STATUS_ERROR;
	}
	//elf_kind() returns the kind of object associated with an Elf han-dle.
	if (elf_kind(e) != ELF_K_ELF)
	{
		PRINT_ERROR("Is not an ELF object\n");
		return STATUS_ERROR;
	}
//elf getshdrstrndx() allows our program to work correctly when the object being examined has a very large number of sections.

	if (elf_getshdrstrndx(e, shstrndx) != 0)
	{
		PRINT_ERROR("Fail getting the header\n");
		return STATUS_ERROR;
	}

	*source = e;
	return STATUS_SUCCESS;
}
/*! \fn print_symbols(Elf *elf, Elf_Scn *scn, GElf_Shdr *shdr)
 \brief Prints section data
 \param elf Object elf
 \param scn Section content
 \param shdr ELF Section Header Table
 */

void print_symbols(Elf *elf, Elf_Scn *scn, GElf_Shdr *shdr)
{
	Elf_Data *data;
	char *name;

	data = 0;
	int number = 0;
	//Elf Data descriptors associated with the section in 	question, verifies if the section has data
	if ((data = elf_getdata(scn, data)) == 0 || data->d_size == 0)
	{
		/* error or no data */
		fprintf(stderr, "Section had no data!n");
		//exit(-1);
	}
	/*now print the symbols*/
	Elf32_Sym *esym = (Elf32_Sym*) data->d_buf;
	Elf32_Sym *lastsym = (Elf32_Sym*) ((char*) data->d_buf + data->d_size);
	/* now loop through the symbol table and print it*/
	for (; esym < lastsym; esym++)
	{
		if ((esym->st_value == 0) || (ELF32_ST_BIND(esym->st_info) == STB_WEAK)
				|| (ELF32_ST_BIND(esym->st_info) == STB_NUM)
				|| (ELF32_ST_TYPE(esym->st_info) != STT_FUNC))
			continue;
		name = elf_strptr(elf, shdr->sh_link, (size_t) esym->st_name);
		if (!name)
		{
			fprintf(stderr, "%sn", elf_errmsg(elf_errno()));
			//exit(-1);
		}
		printf("%d: %sn", number++, name);
	}
}

/*! \fn printHeaders(int fd)
 \brief Prints on display the ELF headers of the input file
 \param fd pointer to the input file
 */
char printHeaders(int fd)
{

	Elf * e = 0;
	char * name;
	Elf_Scn * scn;
	GElf_Shdr shdr;
	size_t shstrndx;

	validateElf(&e, fd, &shstrndx);
	ReadingELFExecutableHeader(e, fd);
	scn = NULL;

	while ((scn = elf_nextscn(e, scn)) != NULL )
	{
		if (gelf_getshdr(scn, &shdr) != &shdr)
		{
			PRINT_ERROR("getshdr () failed : % s . ", elf_errmsg(-1));
			return STATUS_ERROR;
		}

		//print_symbols(e,scn,&shdr);
		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL )
		{
			PRINT_ERROR("elf_strptr () failed : % s . ", elf_errmsg(-1));
		}

		//printf("Section: %x, %s\n", (unsigned int) elf_ndxscn(scn), name);
		//(void) printf(" Section % -4.4 jd % s \ n ", (uintmax_t) elf_ndxscn(scn), name);
	}

	return STATUS_SUCCESS;
}
/*! \fn processHeaders(int fd)
 \brief Process ELF headers of the input file
 \param fd pointer to the input file
 */
char processHeaders(int fd)
{

	Elf * e = 0;
	Elf_Scn * scn;
	GElf_Shdr shdr;
	size_t shstrndx;

	validateElf(&e, fd, &shstrndx);

	scn = NULL;

	while ((scn = elf_nextscn(e, scn)) != NULL )
	{
		if (gelf_getshdr(scn, &shdr) != &shdr)
		{
			PRINT_ERROR("getshdr () failed : % s . ", elf_errmsg(-1));
			return STATUS_ERROR;
		}

		processHeader(shdr, e, shstrndx, fd);
	}

	return STATUS_SUCCESS;
}
/*! \fn ReadingELFExecutableHeader(Elf *e, int fd)
 \brief Displays the ELF file characteristics established in its header
 \param e ELF object containing the header information
 \param fd pointer to the input file
 */
void ReadingELFExecutableHeader(Elf *e, int fd)
{
	GElf_Ehdr ehdr;
	int i;
	//char *id;
	//char bytes[8];
	size_t n;

	if (gelf_getehdr(e, &ehdr) == NULL )
		PRINT_ERROR(" getehdr() failed : %s. ", elf_errmsg(-1));
//gelf_getclass retrieves the ELF class of the object being examined. 32bits o 64bits
	if ((i = gelf_getclass(e)) == ELFCLASSNONE)
		PRINT_ERROR(" getclass() failed : %s. ", elf_errmsg(-1));
	(void) printf("%d - bit ELF object \n ", i == ELFCLASS32 ? 32 : 64);
//elf getident function to retrieve the contents of the e_ident[] array from the underlying file.
	/*if ((id = elf_getident(e, NULL )) == NULL )
		PRINT_ERROR(" getident() failed : %s.", elf_errmsg(-1));
	(void) printf(" %3s e_ident [0..%1d ] %7s ", " ", EI_ABIVERSION, " ");*/

	/*for (i = 0; i <= EI_ABIVERSION; i++)
	{
		bytes[i] = (char) id[i];
		(void) printf(" [ ’%s’ %x ] ", bytes, id[i]);
	}*/
	(void) printf(" \n ");
	printf(PRINT_FMT, "e_type", (uintmax_t) ehdr.e_type);
	printf(PRINT_FMT, "e_machine", (uintmax_t) ehdr.e_machine);
	printf(PRINT_FMT, "e_version", (uintmax_t) ehdr.e_version);
	printf(PRINT_FMT, "e_entry", (uintmax_t) ehdr.e_entry);
	printf(PRINT_FMT, "e_phoff", (uintmax_t) ehdr.e_phoff);
	printf(PRINT_FMT, "e_shoff", (uintmax_t) ehdr.e_shoff);
	printf(PRINT_FMT, "e_flags", (uintmax_t) ehdr.e_flags);
	printf(PRINT_FMT, "e_ehsize", (uintmax_t) ehdr.e_ehsize);
	printf(PRINT_FMT, "e_phentsize", (uintmax_t) ehdr.e_phentsize);
	printf(PRINT_FMT, "e_shentsize", (uintmax_t) ehdr.e_shentsize);
	if (elf_getshdrnum(e, &n) != 0)
		PRINT_ERROR(" getshdrnum() failed : %s.", elf_errmsg(-1));
	(void) printf(PRINT_FMT, "(shnum)", (uintmax_t) n);
	if (elf_getshdrstrndx(e, &n) != 0)
		PRINT_ERROR(" getshdrstrndx () failed : %s.", elf_errmsg(-1));
	(void) printf(PRINT_FMT, "(shstrndx)", (uintmax_t) n);
	if (elf_getphdrnum(e, &n) != 0)
		PRINT_ERROR(" getphdrnum() failed : %s.", elf_errmsg(-1));
	(void) printf(PRINT_FMT, "(phnum)", (uintmax_t) n);
	//( void ) elf_end(e);

}
